#ifndef __QUEUE_HPP__
#define __QUEUE_HPP__

#include <stdio.h>
#include <string>
#include <queue>
#include <mutex>
#include <condition_variable>

template<typename T>
class Queue
{
private:
    std::queue<T> _queue;
    std::mutex _mutex;
    std::condition_variable _condition_variable;
    int _max_size;

public:
    Queue<T>() : _max_size(20) {}
    explicit Queue<T>(int max_ize) : _max_size(max_ize) {}

    bool push(T const& data) {
        std::unique_lock <std::mutex> lock(_mutex);
        if ((int)_queue.size() >= _max_size) {
            return false;
        }
        _queue.push(data);
        _condition_variable.notify_one();
        return true;
    }

    bool pop(T &pop_value) {
        std::unique_lock <std::mutex> lock(_mutex);
        if (_queue.empty()) {
            return false;
        }

        pop_value = _queue.front();
        _queue.pop();

        return true;
    }

    int wait(T &pop_value) {
        std::unique_lock <std::mutex> lock(_mutex);
        while (_queue.empty()) {
            _condition_variable.wait(lock);
        }

        pop_value = _queue.front();
        _queue.pop();
        return 0;
    }

    int wait_for(T &pop_value,int timeout_ms) {
        // 获取自系统启动到当前的时间点，避免系统时间更新导致的超时问题
        auto start = std::chrono::steady_clock::now();
        bool is_timeout = false;
        std::unique_lock <std::mutex> lock(_mutex);
        while (_queue.empty()) {
            do {
                if (_condition_variable.wait_for(lock,std::chrono::milliseconds(timeout_ms)) \
                    == std::cv_status::timeout) {
                    auto end = std::chrono::steady_clock::now();
                    std::chrono::milliseconds elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
                    timeout_ms -= elapsed.count();
                    is_timeout = true;
                } else {
                    is_timeout = false;
                    break;
                }
            } while (0 < timeout_ms);

            if (is_timeout == true) {
                return -1;
            }
        }

        pop_value = _queue.front();
        _queue.pop();
        return 0;
    }

    void clear() {
        std::queue<T> empty;
        swap(empty, _queue);
    }

    bool empty() const {
        std::unique_lock <std::mutex> lock(_mutex);
        return _queue.empty();
    }

    bool full() {
        std::unique_lock <std::mutex> lock(_mutex);
        bool ret = false;
        if ((int)_queue.size() >= _max_size) {
            ret = true;
        } else {
            ret = false; 
        }

        return ret;
    }

    int size() const {
        return _queue.size();
    }

    int maxSize() const {
        return _max_size;
    }
};

#endif